#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include "stringop.h"
#include "error.h"
#include "balloc.h"
#include "fileop.h"

/*************************************************************************************/
/*                       FUNCOES DE LEITURA DE INFORMACOES                           */
/*************************************************************************************/

// Parametros: Fname - Nome do arquivo a ser carregado
//             Vec   - Vetor que recebe as strings correspondentes as informacoes do arquivo aberto
int FStringVec(String Fname, String Vec[]) {
    String pt;
    FILE   *f;
    int    i = 0;
    char   info[VET_MAX];

    if((f = fopen(Fname, "r")) == NULL) {
        printf("\n  Erro - O arquivo %s nao pode ser aberto!\n", Fname);
        exit(EXIT_FAILURE);
        system("pause");
    }

    do { // Ignora linhas com caracteres "/", "\n" e "fim de arquivo" e carrega a partir do caracter ":".
        fgets(info, VET_MAX, f);
        if(info[0] != '/' && info[0] != '\n' && !feof(f) && info[0] != '\r') {
            Line(info);
            pt = strpbrk(info,":");
            pt++;
            if(pt != NULL && *pt != '\n') {
                Vec[i] = (String)MALLOC(sizeof(char)*(strlen(pt)+1));
                strcpy(Vec[i],pt);
                i++;
            }
        }
    } while(!feof(f));
    fclose(f);
    return i;
}

// Parametros: Fname - Nome do arquivo a ser carregado
//             nfi   - Numero de campos
//             Vec   - Vetor que recebe as strings correspondentes as informacoes do arquivo aberto
//             Msg   - Vetor das mensagens
ipt* FNumVec(String Fname, int nfi, int ncol, String Msg[]) {
    String pt;
    FILE   *f;
    int    i = 0,
           k = 0,
           l = 0;
    char   info[VET_MAX];
    ipt*   Vec2;

    if((f = fopen(Fname, "r")) == NULL) {
        printf("%s%s%s\n", Msg[26], Fname, Msg[27]);
        return NULL;
    }
    Vec2 = (ipt*)MALLOC((nfi+1) *  sizeof(int));
    for(l = 0; l < (nfi+1); l++)
        Vec2[l] = (ipt)MALLOC(sizeof(int) * ncol);

    do { // Ignora linhas com caracteres "/", "\n" e "fim de arquivo" e carrega a partir do caracter ":".
        fgets(info, VET_MAX, f);
        if(*info != '/' && *info != '\n' && !feof(f) && *info != '\r') {
            Line(info);
            pt = info;
            while(*pt != '\n' && *pt != '\r' && pt != NULL && *pt != '\0') {
                pt = strpbrk(pt,":");
                pt++;
                if(pt != NULL && *pt != '\n' && *pt != '\0') {
                    Vec2[i][k] = atoi(pt);
                    k++;
                }
            }
            k = 0;
            i++;
        }
    } while(!feof(f));
    fclose(f);
    return Vec2;
}

// Parametros: Fname - Nome do arquivo a ser carregado
//             key   - Chave pela qual deseja-se organizar o arquivo
//             Msg   - Vetor das mensagens
ipt FKeyLoc(String Fname, String key, String Msg[]) {
    ipt    Keyinfo;
    String pt;
    char   info[VET_MAX];
    int    achou = false;
    FILE   *f;

    Keyinfo = (ipt)MALLOC(sizeof(int)*2);

    if((f = fopen(Fname, "r")) == NULL) {
        printf("%s%s%s\n", Msg[26], Fname, Msg[27]);
        return NULL;
    }

    do {
        fgets(info, VET_MAX, f);
        pt = info;
        if(*info != '/' && *info != '\n' && !feof(f) && *info != '\r') {
            pt = strpbrk(pt,":");
            *pt = '\0';
            pt++;
            if(strcmp(info,key) == 0) {
                Keyinfo[0] = atoi(pt);
                pt = strpbrk(pt,":");
                pt++;
                pt = strpbrk(pt,":");
                pt++;
                pt = strpbrk(pt,":");
                pt++;
                Keyinfo[1] = atoi(pt);
                achou = true;
            }
        }
    } while(!feof(f) && achou == false);

    if(achou == false) {
        printf("Erro - Campo nao encontrado");
        FREE(Keyinfo);
        return NULL;
    }
    fclose(f);
    return Keyinfo;
}

/*************************************************************************************/
/*                             FUNCOES DE LISTAGEM                                   */
/*************************************************************************************/

// Parametros: Saida  - Nome do arquivo de saida
//             Config - Vetor contendo os rotulos dos campos
//             Finfo  - Vetor contendo o tamanho dos campos
//             Msg    - Vetor das mensagens
void ListaSaida(String Saida, String Config[], ipt* Finfo, String Msg[]) {
    String vet,
           brk = "\0",
           campo;
    FILE   *f;
    char   info[VET_MAX];
    int    i = 0;

    if((f = fopen(Saida, "r")) == NULL) {
        printf("%s%s%s\n", Msg[26], Saida, Msg[27]);
        fclose(f);
        return;
    }

    fgets(info,VET_MAX,f); //pega um registro
    brk = "\0";
    vet = info;
    if(feof(f)) {
        fclose(f);
        return;
    }

    printf("\n\n%s\n\n", Msg[28]);
    printf("%s\n\n", Msg[29]);
    printf("%s\n", Msg[30]);

    do {
        if(*vet != '$') { // Testa se o registro tem marca de remocao
            i = 0;
            printf("\n--------------------------------------------------------\n\n");
            while(*brk != '\n') {           
                printf("  %s", Config[i+5]); // Imprime rotulo
                poeBranco(10-strlen(Config[i+5]));
                printf("%s", campo = pegaCampo(vet, &brk, "|#")); // Imprime campo        
                poeBranco(25-strlen(campo));
                vet = brk;
                printf("%2d     %2d     %2d", Finfo[i][0], Finfo[i][1], Finfo[i][2]);
                printf("\n");
                i++;
            }
        }
        fgets(info,VET_MAX,f);
        brk = "\0";
        vet = info;
    } while(!feof(f));
    fclose(f);
}

// Parametros: Entrada - Nome do arquivo de entrada
//             Config  - Vetor contendo os rotulos dos campos
//             inf     - Apontador para matriz com informacoes sobre os campos
//             Msg     - Vetor das mensagens
void ListaEntrada(String Entrada, String Config[], ipt* inf, String Msg[]) {
    FILE *f;
    char info[VET_MAX];
    int  i = 0, j = 0, k = 0, x = 0, // Contadores
         pos = inf[atoi(Config[4])][0]-2; // Tamanho do registro

    if((f = fopen(Entrada , "r")) == NULL) {
        printf("%s%s%s\n", Msg[26], Entrada, Msg[27]);
        return;
    }

    printf("\n\n%s\n\n", Msg[28]);
    printf("%s\n\n", Msg[29]);
    printf("%s\n", Msg[30]);

    fgets(info,VET_MAX,f); // Grava um registro em info 
    do { // Impressao dos registros na tela  
        printf("\n--------------------------------------------------------\n\n");
        while (k < pos) {
            printf("  %s", Config[i+5]); // Imprime rotulo
            poeBranco(10-strlen(Config[i+5]));
            j = j + inf[i][0];
            while (k < j) {
                printf("%c", info[k]); // Imprime campo        
                k++;        
            }
            poeBranco(25-(k - x));
            printf("%2d     %2d     %2d", inf[i][0], inf[i][1], inf[i][2]);
            x = k;
            printf("\n");
            i++;
        }
        k = 0; j = 0; i = 0; x = 0; // Zera contadores
        fgets(info,VET_MAX,f); // Grava um registro em info  
    } while(!feof(f));
    fclose(f);
}

// Parametros: arq - Nome do arquivo de chaves
//             Msg - Vetor das mensagens
void LeKey(String arq, String Msg[]) {
    String vet,
           brk;
    FILE   *f;
    char   info[VET_MAX];

    if((f = fopen(arq, "r")) == NULL) {
        printf("%s%s%s\n", Msg[26], arq, Msg[27]);
        return;
    }

    fgets(info, VET_MAX, f);
    vet = info;
    printf("\n%s\n", Msg[31]);
    while(!feof(f)) {
        printf("  %s", pegaCampo(vet, &brk, "-")); // Imprime campo
        vet = brk; 
        printf("    %s", pegaCampo(vet, &brk, "\n")); // Imprime campo 
        vet = brk;
        printf("\n");
        fgets(info, VET_MAX, f);
        vet = info; 
    }
    fclose(f);
}

/*************************************************************************************/
/*                               FUNCOES DE CONVERSAO                                */
/*************************************************************************************/

// Parametros: Entrada - Nome do arquivo de entrada
//             Saida   - Nome do arquivo de saida
//             Config  - Vetor com configuracoes
//             teste   - Apontador para matriz com informacoes sobre campos
//             Msg     - Vetor com mensagens
Boolean FixVar(String Entrada, String Saida, String Config[], ipt* teste, String Msg[]) {
    String Reg = NULL;
    FILE    *f,
            *g;
    int     i = 0, k = 0, l = 0, // Contadores
            nfi = atoi(Config[4]), // Numero de campos
            pos = teste[nfi][0]; // Tamanho total do registro
    Boolean f_field = false; // flag que impede um registro com erro de ser colocado

    if((f = fopen(Entrada , "r")) == NULL) {
        printf("%s%s%s\n", Msg[26], Entrada, Msg[27]);
        return false;
    }
    if((g = fopen(Saida , "w")) == NULL) {
        printf("%s%s%s\n", Msg[26], Saida, Msg[27]);
        return false;
    }

    while(!feof(f)) {
        k = 0;
        f_field = false;
        fseek(f, i*pos, SEEK_SET);
        f_field = RegTest(f, Config, teste, Msg,i);
        fseek(f, i*pos, SEEK_SET);
        Reg = RegRead(f, teste[k][0]);
        if(!f_field) {
            while(k < nfi-1 && Reg != NULL) {
                if(Reg != NULL) {
                    fwrite(Reg,1,strlen(Reg),g);
                    if(k < nfi-2) fwrite(Config[1],1,1,g);
                    for(l = 0; l < teste[k][0]; l++) Reg[l] = '\0';
                    FREE(Reg);
                    Reg = NULL;
                }
                k++;
                if(k < nfi) Reg = RegRead(f,teste[k][0]);
            }
            if(Reg != NULL) {
                if(strcmp(Reg,"#") == 0) {
                    fwrite(Reg,1,strlen(Reg),g);
                    FREE(Reg);
                    fputs("\n",g);
                }
            }
        }
        if(Reg != NULL) FREE(Reg);
        i++;
    }
    fclose(f);
    fclose(g);
    return true;
}

// Parametros: Reader - Nome do arquivo de saida a ser lido
//             Keys   - Nome do arquivo com as chaves desorganizadas
//             Sep    - Separador usado no arquivo de saida
//             Msg    - Vetor das mensagens
Boolean ExKeys(String Reader, String Keys, String Sep, String Msg[]) {
    int i = 0, pos = 0;
    char info[VET_MAX];
    String pt; 
    FILE *f, *g;

    if((f = fopen(Reader , "r")) == NULL) {
        printf("%s%s%s\n", Msg[26], Reader, Msg[27]);
        return false;
    }
    if((g = fopen(Keys , "w")) == NULL) {
        printf("%s%s%s\n", Msg[26], Keys, Msg[27]);
        return false;
    }

    pos = ftell(f);
    fgets(info, VET_MAX, f);
    while(!feof(f)) {
        pt = strtok(info, Sep);
        if(*pt != '$') { // Testa se o registro tem marca de remocao
            fprintf(g,"%06d - %d\n", atoi(pt), pos);
            i++;
        }
        pos = ftell(f);
        fgets(info, VET_MAX, f);
    }
    fclose(f);
    fclose(g);
    return true;
}

/*************************************************************************************/
/*                           FUNCOES DE BUSCA - OPERACAO                             */
/*************************************************************************************/

// Parametros: Search -
//             File   -
//             Loc    -
//             Config -
//             Msg    - Vetor das mensagens
void RegSP(String Search, String File, int Loc, String Config[], String Msg[]) {
    char info[VET_MAX];
    FILE *f;

    if((f = fopen(File, "r")) == NULL) {
        printf("%s%s%s\n", Msg[26], File, Msg[27]);
        fclose(f);
        return;
    }
    if(Loc >= 0) {
        fseek (f, Loc , SEEK_SET );
        fgets(info, VET_MAX, f);
        RegPrint(info,Config);
    }
    else printf("\n%s\n", Msg[32]);  // nao achou
    fclose(f);
    return;
}

// Parametros: Search -
//             File   -
//             Loc    -
//             Config -
//             Msg    - Vetor das mensagens
void RegRemv(String Search, String File, int Loc, String Config[], String Msg[]) {
    FILE *f;

    if((f = fopen(File, "r+")) == NULL) {
        printf("%s%s%s\n", Msg[26], File, Msg[27]);
        fclose(f);
        return;
    }
    if(Loc >= 0) {
        fseek(f, Loc , SEEK_SET );
        fprintf(f, "$");
        printf("\n%s\n", Msg[33]);
    }
    else printf("\n%s\n", Msg[32]);  // nao achou
    fclose(f);
    return;
}

/*************************************************************************************/
/*                                FUNCOES AUXILIARES                                 */
/*************************************************************************************/

// Parametros: f      -
//             Narq   -
void Ferase(FILE *f, String Narq) {
    fclose(f);
    f = fopen(Narq , "w");
    fclose(f);
    return;
}

// Parametros: Registro - Registro que deseja-se imprimir
//             Config   - Usos diversos
void RegPrint(String Registro, String Config[]) {
    String vet, brk = "\0";
    char copia[VET_MAX];
    int i = 0;

    strcpy(copia, Registro);
    vet = copia; 
    printf("_________________\n\n");
    while(*brk != '\n') {                    
        printf(" %s = ", Config[i+5]); // Imprime rotulo
        printf("%s", pegaCampo(vet, &brk, "|#")); // Imprime campo        
        i++;
        vet = brk;  
        printf("\n");             
    }
}

// Parametros: Search - Chave a ser procurada
//             File   - Nome do arquivo no qual se busca a chave (saida)
//             Config - Usos diversos
//             Msg    - Vetor das mensagens
int RegSearch(String Search, String File, String Config[], String Msg[]) {
    String pt;
    char   info[VET_MAX];
    int    pos = 0; // Posicao do Registro buscado. Se nao for encontrado, devolvera -1
    FILE   *f;

    if((f = fopen(File, "r+")) == NULL) {
        printf("%s%s%s\n", Msg[26], File, Msg[27]);
        fclose(f);
        return -1;
    }
    pos = ftell(f);
    fgets(info, VET_MAX, f);
    while(!feof(f)) {
        pt = strtok(info, Config[1]);
        if(strcmp(pt,Search) == 0) return pos+1; // achou
        pos = ftell(f);
        fgets(info, VET_MAX, f);
    }
    return -1;
}

// Parametros: Search - Chave a ser procurada
//             File   - Nome do arquivo no qual se busca a chave (saida)
//             Config - Usos diversos
//             Msg    - Vetor das mensagens
int RegSearchBin(String Search, String File, String Config[], String Msg[]) {
    char info[VET_MAX];
    int  m = 0,
         n = 0,
         meio = 0,
         key = atoi(Search),
         indice[VET_MAX][2];
    FILE *f, *g;

    if((f = fopen(File, "r+")) == NULL) {
        printf("%s%s%s\n", Msg[26], File, Msg[27]);
        fclose(f);
        return -1;
    }
    if((g = fopen("keys_sorted.txt", "r")) == NULL) {
        printf("%s%s%s\n", Msg[26], "keys_sorted.txt", Msg[27]);
        fclose(g);
        return -1;
    }
    fgets(info, VET_MAX, g);
    while(!feof(g)) {
        indice[m][0] = atoi(strtok(info, " -\n"));
        indice[m][1] = atoi(strtok(NULL, " -\n"));
        m++;
        fgets(info, VET_MAX, g);
    }
    while (n <= m) {
        meio = (n+m)/2;
        if (key == indice[meio][0]) return indice[meio][1];
        else if (key < indice[meio][0]) m = meio-1;
        else n = meio+1;
    }
    return -1;
}

// Parametros: f - Arquivo a ser lido
//             k - Tamanho do campo
String RegRead(FILE *f, int k) {
    String field;

    field = (String)MALLOC(sizeof(char)*(k+1));
    field[k] = '\0';
    fread(field,1,k,f);
    SpaceBreak(field," ");
    if(feof(f)) {
        FREE(field);
        return NULL;
    }
    return field;
}

// Parametros: f      - Arquivo a ser lido
//             Config - Tamanho do campo
//             teste  -
//             Msg    -
//             i      -
Boolean RegTest(FILE *f, String Config[], ipt* teste, String Msg[], int i) {
    String  Reg = NULL;
    int     k = 0, // Contadores
            nfi = atoi(Config[4]); // Numero de campos
    Boolean flag = false, // flag que impede um registro com erro de ser colocado
            av = false;

    while(k<nfi && flag == false) {
        Reg = RegRead(f, teste[k][0]);
        if(Reg != NULL && strcmp(Reg,"")==0 && teste[k][2] == 1) {
            printf("\n%s'%s' - %do %s", Msg[34], Config[5+k], i+1, Msg[35]);
            flag = true;
            av = true;
        }
        else if(Reg !=NULL && FieldError(Reg,teste[k][1],Msg)) {
            printf("\n%s'%s' - %do%s%d)", Msg[34], Config[5+k], i+1, Msg[37], teste[k][1]);
            flag = true;
            av = true;
        }
        if(Reg != NULL) FREE(Reg);
        k++;
    }
    return av;
}
